classdef ModelManagementService < handle
    % MODELMANAGEMENTSERVICE 模型管理服务
    % 基于DO-178规则第4条（架构分离原则）设计
    % 负责协调所有Simulink模型管理相关的业务逻辑
    %
    % 功能：
    %   - 模型列表管理（获取、刷新、过滤）
    %   - 模型操作（打开、创建、导入、删除）
    %   - 模型依赖分析（依赖关系、库依赖）
    %   - 模型树管理（构建、刷新、高亮）
    %   - 模型状态查询（检测模型状态、分类信息）
    %
    % 设计原则：
    %   - 单一职责：只负责模型管理业务逻辑协调
    %   - 依赖注入：依赖现有的工具函数，不重复实现
    %   - 错误明确：遵循规则第2条，不使用silent fallback
    %   - 可测试：所有公共方法都可以独立测试
    %
    % 使用示例：
    %   % 获取单例实例
    %   service = ModelManagementService.getInstance();
    %   
    %   % 获取模型列表
    %   modelList = service.getModelList();
    %   
    %   % 打开模型
    %   result = service.openModel('MyModel');
    %   
    %   % 创建新模型
    %   result = service.createModel('NewModel', 'Nonreusable');
    %   
    %   % 查看模型依赖
    %   service.viewDependency('MyModel', 'model');
    %
    % 作者: MBD Platform Team
    % 日期: 2025-10-26
    % 遵循DO-178标准
    
    properties (Access = private)
        projectRoot         % 项目根目录
        logger              % LoggingService实例
        currentModelCache   % 当前模型缓存
        modelListCache      % 模型列表缓存
        cacheTimestamp      % 缓存时间戳
        cacheTimeout        % 缓存超时时间（秒）
    end
    
    methods (Access = private)
        function obj = ModelManagementService()
            % 私有构造函数 - 初始化服务（单例模式）
            % 基于规则第2条：不使用silent fallback，明确报告错误
            
            % 初始化日志服务
            try
                obj.logger = LoggingService.getInstance();
            catch ME
                warning('LoggingService:Unavailable', '日志服务不可用: %s', ME.message);
                obj.logger = [];
            end
            
            obj.cacheTimeout = 30; % 30秒缓存超时
            obj.cacheTimestamp = struct('modelList', 0);
            obj.modelListCache = {};
            obj.currentModelCache = '';
            
            try
                % 获取项目根目录
                obj.projectRoot = pwd;
                fprintf('✅ ModelManagementService已初始化\n');
                fprintf('   项目根目录: %s\n', obj.projectRoot);
                
                if ~isempty(obj.logger)
                    obj.logger.logInfo('ModelManagementService已初始化');
                end
            catch ME
                if ~isempty(obj.logger)
                    obj.logger.logError(['模型管理服务初始化失败: ', ME.message]);
                end
                % error() 的格式化字符串使用 %s 避免转义字符问题
                error('ModelManagementService:InitFailed', ...
                    '无法初始化模型管理服务：%s', ME.message);
            end
        end
    end
    
    methods (Static, Access = public)
        function obj = getInstance()
            % 获取ModelManagementService的单例实例
            % 输出：
            %   obj - ModelManagementService单例实例
            % 基于规则第2条：明确报告错误
            
            persistent instance;
            
            if isempty(instance) || ~isvalid(instance)
                instance = ModelManagementService();
                fprintf('✅ 创建ModelManagementService单例实例\n');
            end
            
            obj = instance;
        end
    end
    
    methods (Access = public)
        %% ==================== 模型列表管理 ====================
        
        function modelList = getModelList(obj, forceRefresh)
            % 获取项目中的Simulink模型列表
            % 输入：
            %   forceRefresh - (可选) 是否强制刷新缓存，默认false
            % 输出：
            %   modelList - 模型名称的cell array
            % 基于规则第2条：明确报告错误，不返回默认值
            
            if nargin < 2
                forceRefresh = false;
            end
            
            % 检查缓存是否有效
            if ~forceRefresh && obj.isCacheValid('modelList')
                fprintf('✅ 从缓存加载模型列表 (共%d个模型)\n', length(obj.modelListCache));
                if ~isempty(obj.logger)
                    obj.logger.logInfo(sprintf('从缓存加载模型列表 (共%d个模型)', length(obj.modelListCache)));
                end
                modelList = obj.modelListCache;
                return;
            end
            
            try
                fprintf('🔍 正在扫描项目中的Simulink模型...\n');
                if ~isempty(obj.logger)
                    obj.logger.logInfo('开始扫描项目中的Simulink模型');
                end
                
                % 调用现有的getModelList函数
                % 注意：这个函数在tools/apps目录下
                modelList = getModelList();
                
                if isempty(modelList)
                    fprintf('⚠️ 未在项目中检测到任何有效的Simulink模型\n');
                    if ~isempty(obj.logger)
                        obj.logger.logWarning('未检测到任何有效的Simulink模型');
                    end
                    modelList = {};
                else
                    fprintf('✅ 成功检测到 %d 个模型: %s\n', ...
                        length(modelList), strjoin(modelList, ', '));
                    if ~isempty(obj.logger)
                        obj.logger.logInfo(sprintf('成功检测到 %d 个模型', length(modelList)));
                    end
                end
                
                % 更新缓存
                obj.modelListCache = modelList;
                obj.cacheTimestamp.modelList = now;
                
            catch ME
                error('ModelManagementService:GetModelListFailed', ...
                    '获取模型列表失败：%s', ME.message);
            end
        end
        
        function modelInfo = getModelInfo(obj, modelName)
            % 获取指定模型的详细信息
            % 输入：
            %   modelName - 模型名称
            % 输出：
            %   modelInfo - 包含模型信息的结构体
            
            if nargin < 2 || isempty(modelName)
                error('ModelManagementService:InvalidInput', '模型名称不能为空');
            end
            
            try
                fprintf('🔍 正在获取模型信息: %s\n', modelName);
                
                % 获取模型完整路径
                modelPath = which(modelName);
                
                if isempty(modelPath)
                    error('模型 "%s" 不存在或不在MATLAB路径中', modelName);
                end
                
                % 获取文件信息
                fileInfo = dir(modelPath);
                
                % 获取模型校验和（如果模型已加载）
                checksum = '';
                if bdIsLoaded(modelName)
                    try
                        checksum = getModelChecksum(modelName);
                    catch
                        checksum = '未知';
                    end
                end
                
                % 构建模型信息结构体
                modelInfo = struct();
                modelInfo.name = modelName;
                modelInfo.path = modelPath;
                modelInfo.folder = fileparts(modelPath);
                modelInfo.size = fileInfo.bytes;
                modelInfo.dateModified = fileInfo.date;
                modelInfo.checksum = checksum;
                modelInfo.isLoaded = bdIsLoaded(modelName);
                
                fprintf('✅ 模型信息获取成功\n');
                
            catch ME
                error('ModelManagementService:GetModelInfoFailed', ...
                    '获取模型信息失败：%s', ME.message);
            end
        end
        
        %% ==================== 模型操作 ====================
        
        function result = openModel(obj, modelName)
            % 打开指定的Simulink模型
            % 输入：
            %   modelName - 模型名称
            % 输出：
            %   result - 包含成功标志和消息的结构体
            % 基于规则第2条：明确报告错误
            
            result = struct('success', false, 'message', '');
            
            if nargin < 2 || isempty(modelName)
                error('ModelManagementService:InvalidInput', '模型名称不能为空');
            end
            
            try
                fprintf('📂 正在打开模型: %s\n', modelName);
                if ~isempty(obj.logger)
                    obj.logger.logInfo(sprintf('开始打开模型: %s', modelName));
                end
                
                % 检查模型是否已加载
                if bdIsLoaded(modelName)
                    fprintf('⚠️ 模型已加载，将其置于前台\n');
                    % 即使已加载，也要调用 open_system 确保窗口显示并置于前台
                    open_system(modelName);
                    result.success = true;
                    result.message = sprintf('模型 "%s" 已打开', modelName);
                    return;
                end
                
                % 打开模型
                open_system(modelName);
                
                % 验证模型是否成功打开
                if bdIsLoaded(modelName)
                    fprintf('✅ 模型打开成功: %s\n', modelName);
                    if ~isempty(obj.logger)
                        obj.logger.logInfo(sprintf('模型打开成功: %s', modelName));
                    end
                    result.success = true;
                    result.message = sprintf('成功打开模型: %s', modelName);
                    obj.currentModelCache = modelName;
                else
                    error('模型打开失败，但未报告具体错误');
                end
                
            catch ME
                result.success = false;
                result.message = ['打开模型失败: ', ME.message];
                if ~isempty(obj.logger)
                    obj.logger.logError(['打开模型失败 [', modelName, ']: ', ME.message]);
                end
                error('ModelManagementService:OpenModelFailed', '%s', result.message);
            end
        end
        
        function result = createModel(obj, modelName, template)
            % 创建新的Simulink模型
            % 输入：
            %   modelName - 新模型名称
            %   template - 模板类型：'Nonreusable'（单实例）或 'Reusable'（多实例，默认）
            % 输出：
            %   result - 包含成功标志和消息的结构体
            % 基于规则第2条：明确报告错误
            
            result = struct('success', false, 'message', '');
            
            if nargin < 2 || isempty(modelName)
                error('ModelManagementService:InvalidInput', '模型名称不能为空');
            end
            
            if nargin < 3
                template = 'Reusable'; % 默认为多实例模型
            end
            
            try
                fprintf('🔨 正在创建新模型: %s (模板: %s)\n', modelName, template);
                
                % 验证模型名称是否有效
                if ~isvarname(modelName)
                    result.message = ['模型名称 "', modelName, '" 不是有效的MATLAB变量名'];
                    return;
                end
                
                % 检查模型是否已存在
                if exist(modelName, 'file')
                    result.message = ['模型 "', modelName, '" 已经存在'];
                    return;
                end
                
                % 调用现有的addModelFolder函数
                if strcmpi(template, 'Nonreusable')
                    addModelFolder(modelName, 'Nonreusable');
                else
                    addModelFolder(modelName);
                end
                
                fprintf('✅ 模型创建成功: %s\n', modelName);
                result.success = true;
                result.message = sprintf('成功创建模型: %s', modelName);
                
                % 清除缓存，强制下次刷新
                obj.clearCache();
                
            catch ME
                result.success = false;
                % 使用字符串拼接避免转义字符问题（特别是Windows路径中的反斜杠）
                result.message = ['创建模型失败: ', ME.message];
                % Service层不抛出错误，只返回结果（基于规则第4条：架构分离）
            end
        end
        
        function result = importModel(obj, sourcePath, targetName, template)
            % 导入外部Simulink模型到项目
            % 输入：
            %   sourcePath - 源模型完整路径
            %   targetName - (可选) 目标模型名称，默认使用源文件名
            %   template - (可选) 模板类型
            % 输出：
            %   result - 包含成功标志和消息的结构体
            % 基于规则第2条和第4条：Service层返回结果，不抛出异常
            
            result = struct('success', false, 'message', '');
            
            if nargin < 2 || isempty(sourcePath)
                result.message = '源模型路径不能为空';
                return;
            end
            
            if nargin < 3 || isempty(targetName)
                [~, targetName, ~] = fileparts(sourcePath);
            end
            
            if nargin < 4
                template = 'Reusable';
            end
            
            try
                fprintf('📥 正在导入模型: %s -> %s\n', sourcePath, targetName);
                
                % 验证源文件是否存在
                if ~exist(sourcePath, 'file')
                    result.message = ['源模型文件不存在: ', sourcePath];
                    return;
                end
                
                % 验证目标模型名称
                if ~isvarname(targetName)
                    result.message = ['目标模型名称 "', targetName, '" 不是有效的MATLAB变量名'];
                    return;
                end
                
                % 检查目标模型是否已存在
                if exist(targetName, 'file')
                    result.message = ['目标模型 "', targetName, '" 已经存在'];
                    return;
                end
                
                % 先创建模型文件夹结构
                if strcmpi(template, 'Nonreusable')
                    addModelFolder(targetName, 'Nonreusable');
                else
                    addModelFolder(targetName);
                end
                
                % 获取目标路径
                designDir = ProjectPathManager.getDirPath('design root');
                targetFolder = fullfile(designDir, targetName, 'specification');
                targetPath = fullfile(targetFolder, [targetName '.slx']);
                
                % 复制模型文件
                copyfile(sourcePath, targetPath);
                
                fprintf('✅ 模型导入成功: %s\n', targetName);
                result.success = true;
                result.message = sprintf('成功导入模型: %s', targetName);
                
                % 清除缓存
                obj.clearCache();
                
            catch ME
                result.success = false;
                result.message = ['导入模型失败: ', ME.message];
                % Service层不抛出错误，只返回结果
            end
        end
        
        function result = deleteModel(obj, modelName)
            % 删除指定的Simulink模型及其相关文件夹
            % 输入：
            %   modelName - 模型名称（可以是cell array，批量删除）
            % 输出：
            %   result - 包含成功标志和消息的结构体
            % 基于规则第2条和第4条：Service层返回结果，不抛出异常
            
            % 初始化返回结果
            result = struct('success', false, 'message', '', 'deletedModels', {{}});
            deletedModels = {};
            
            if nargin < 2 || isempty(modelName)
                result.message = '模型名称不能为空';
                return;
            end
            
            % 统一转换为cell array处理
            if ischar(modelName)
                modelName = {modelName};
            end
            
            try
                fprintf('🗑️ 正在删除模型...\n');
                
                for i = 1:length(modelName)
                    currentModel = modelName{i};
                    fprintf('   删除: %s\n', currentModel);
                    
                    % 先从项目中移除模型引用
                    removeModelFolder(currentModel);
                    
                    % 删除模型文件夹
                    deleteModelFolder(currentModel);
                    
                    deletedModels{end+1} = currentModel;
                end
                
                fprintf('✅ 成功删除 %d 个模型\n', length(deletedModels));
                result.success = true;
                result.message = sprintf('成功删除 %d 个模型', length(deletedModels));
                result.deletedModels = deletedModels;
                
                % 清除缓存
                obj.clearCache();
                
            catch ME
                % 确保result结构体已初始化
                result.success = false;
                result.message = ['删除模型失败: ', ME.message];
                result.deletedModels = deletedModels;
                % Service层不抛出错误，只返回结果
            end
        end
        
        %% ==================== 模型依赖分析 ====================
        
        function viewDependency(obj, modelName, dependencyType)
            % 查看模型依赖关系
            % 输入：
            %   modelName - 模型名称
            %   dependencyType - 依赖类型：'model'（模型依赖）或 'file'（文件依赖）
            % 基于规则第2条：明确报告错误
            
            if nargin < 2 || isempty(modelName)
                error('ModelManagementService:InvalidInput', '模型名称不能为空');
            end
            
            if nargin < 3
                dependencyType = 'model';
            end
            
            try
                fprintf('🔍 正在分析 %s 的依赖关系...\n', modelName);
                
                % 调用现有的依赖分析函数
                if strcmpi(dependencyType, 'model')
                    viewModelDependency(modelName);
                elseif strcmpi(dependencyType, 'file')
                    viewFileDependency(modelName);
                else
                    error('不支持的依赖类型: %s（支持: model, file）', dependencyType);
                end
                
                fprintf('✅ 依赖关系分析完成\n');
                
            catch ME
                error('ModelManagementService:ViewDependencyFailed', ...
                    '查看依赖关系失败：%s', ME.message);
            end
        end
        
        function libDeps = getLibraryDependencies(obj, modelName)
            % 获取模型的库依赖关系
            % 输入：
            %   modelName - 模型名称
            % 输出：
            %   libDeps - 库依赖信息的结构体数组
            
            if nargin < 2 || isempty(modelName)
                error('ModelManagementService:InvalidInput', '模型名称不能为空');
            end
            
            try
                fprintf('🔍 正在获取 %s 的库依赖...\n', modelName);
                
                % 调用现有的getLibDependency函数
                libDeps = getLibDependency(modelName);
                
                if isempty(libDeps)
                    fprintf('⚠️ 未检测到库依赖\n');
                else
                    fprintf('✅ 检测到 %d 个库依赖\n', length(libDeps));
                end
                
            catch ME
                error('ModelManagementService:GetLibDependencyFailed', ...
                    '获取库依赖失败：%s', ME.message);
            end
        end
        
        %% ==================== 模型树管理 ====================
        
        function refreshModelTree(obj, treeHandle)
            % 刷新模型树
            % 输入：
            %   treeHandle - uitree控件句柄
            % 基于规则第2条：明确报告错误
            
            if nargin < 2 || isempty(treeHandle) || ~isvalid(treeHandle)
                error('ModelManagementService:InvalidInput', '树控件句柄无效');
            end
            
            try
                fprintf('🌲 正在刷新模型树...\n');
                if ~isempty(obj.logger)
                    obj.logger.logInfo('开始刷新模型树');
                end
                
                % 调用现有的refreshFolderTree函数
                refreshFolderTree(treeHandle);
                
                fprintf('✅ 模型树刷新完成\n');
                if ~isempty(obj.logger)
                    obj.logger.logInfo('模型树刷新完成');
                end
                
            catch ME
                if ~isempty(obj.logger)
                    obj.logger.logError(['刷新模型树失败: ', ME.message]);
                end
                % error() 的格式化字符串使用 %s 避免转义字符问题
                error('ModelManagementService:RefreshTreeFailed', ...
                    '刷新模型树失败：%s', ME.message);
            end
        end
        
        %% ==================== 缓存管理 ====================
        
        function clearCache(obj)
            % 清除所有缓存
            obj.modelListCache = {};
            obj.currentModelCache = '';
            obj.cacheTimestamp = struct('modelList', 0);
            fprintf('✅ 模型管理服务缓存已清空\n');
        end
        
        function valid = isCacheValid(obj, cacheType)
            % 检查缓存是否有效
            % 输入：
            %   cacheType - 缓存类型（如 'modelList'）
            % 输出：
            %   valid - 缓存是否有效
            
            if ~isfield(obj.cacheTimestamp, cacheType)
                valid = false;
                return;
            end
            
            elapsedTime = (now - obj.cacheTimestamp.(cacheType)) * 86400; % 转换为秒
            valid = elapsedTime < obj.cacheTimeout;
        end
        
        %% ==================== 验证方法 ====================
        
        function isValid = validateModelName(~, modelName)
            % 验证模型名称是否有效
            % 输入：
            %   modelName - 模型名称
            % 输出：
            %   isValid - 是否有效
            
            isValid = false;
            
            if isempty(modelName) || ~ischar(modelName)
                return;
            end
            
            % 模型名称必须是有效的MATLAB变量名
            if ~isvarname(modelName)
                return;
            end
            
            % 不能是特殊名称
            if strcmpi(modelName, '正在检测模型...') || ...
               strcmpi(modelName, '未检测到模型') || ...
               strcmpi(modelName, '无模型')
                return;
            end
            
            isValid = true;
        end
        
        function exists = modelExists(obj, modelName)
            % 检查模型是否存在
            % 输入：
            %   modelName - 模型名称
            % 输出：
            %   exists - 模型是否存在
            
            if ~obj.validateModelName(modelName)
                exists = false;
                return;
            end
            
            modelPath = which(modelName);
            exists = ~isempty(modelPath) && exist(modelPath, 'file') == 4;
        end
    end
end

